softwarefandomcom_de-20200213-history
Fpii
Fpii ist eine funktionale und objektorientierte Programmiersprache, die auf den von John Backus vorgeschlagenen FP-Systemen aufbaut. Die Objektorientierung ist auf die Polymorphie der Methoden in einem Klassenobjekt samt Vererbung reduziert. Fpii unterstützt als variablenfreie Programmiersprache weder globale noch lokale Variablen, was aber durch die Verwendung von dict(ionary)-Objekten kompensiert wird. Die Kombinatoren und anderen Operatoren werden in der Infix-Schreibweise in den Sequenzen verwendet. Das Infix-Schema wurde auch auf die Objekte übertragen, um nur ein Klammernpaar, das der Listendarstellung, einzusetzen. Die Quelltexte werden mittels eines Compilers in eine interne (verkettete) Listendarstellung umgewandelt, es können auch Listen von Listen auftreten, wie das bei der Programmiersprache LISP der Fall ist. Diese interne Darstellung wird dann von einem Interpreter verarbeitet. Nebeneffekte werden statt der Ausgabe der Resultate durch fios-Objekte ausgelöst. __TOC__ In der Regel ist die Verarbeitungsrichtung rechts-vor-links wie bei APL. Eine Ausnahme ist zum Beispiel die if-else-Struktur: p1 -> f1;p2 -> f2;etc Datentypen * (1 + 2) ist ein Beispiel für die Gruppe der Funktions-Terme. * (klasse1 :: element1 element2 element3 ...) ist ein Objekt, erkennbar an dem :: * 12 ist ein Beispiel für float, atomarer Datentyp. * name1 ist ein ident, atomarer Datentyp. * 'A' ist ein Beispiel für char, ein atomarer Datentyp. * ( ) ist der null-Datentyp. Die Datentypen haben eine aktive (als Funktionen) und eine passive Eigenschaft. Anwendungsbeispiele 1:(list :: aa bb cc) --> aa 2:(list :: aa bb cc) --> bb pop:(list :: aa bb cc) --> (list :: bb cc) reverse:(string :: 'hallo') --> (string :: 'ollah') ((1='0')->*pop):(string :: '007') --> (string :: '7') "löscht die führenden Nullen mit einer while-Schleife (->*)" ucase a string :: 'hallo' --> (string :: 'HALLO') "a ist der Mapping-Operator, der ucase auf die string-Elemente anwendet" (inc°2):(list :: 33 44 55) --> 45 "(°) verkettet die Funktion 2 mit dem darauffolgendem Increment" (4,2,1,3,nil):(list :: ein ist satz dies) --> (list :: dies ist ein satz) ((bb#),(cc#),(aa#),nil):(dict :: ( ) aa schlau bb barney cc ist) --> (list :: barney ist schlau) Erzeugen und ausführen von Funktionalen, "delobj" schneidet das "list ::" vorne weg, "on" wendet die erzeugte Funktion an: ((delobj°2,1,3,nil) on 4):(list :: + 1 2 (list :: 22 33)) --> 55 Quicksort Algorithmus: qsort:=(nilp->id; ((qsort°3)++1,qsort°4) °((not°nilp°2)->*1,(pop°2),(1>1°2)->(((1°2),3),4,nil);3,((1°2),4),nil) °1,pop,(nil as _1),(nil as _1),nil) dict-Objekte als Namensräume Ein dict-Objekt hat den Aufbau: (dict :: ... ... ) Mit den Operatoren get und put wird in dem dict-Objekt nach dem entsprechenden Bezeichner gesucht und der zugeordnete Wert hervorgeholt oder gesetzt (dabei wird das dict-Objekt zum Teil neu generiert). Length mit Instanzenvariablen, Beispiel wie der Einsatz von lokalen Variablen mit dict-Objekten kompensiert wird: vlength:=((y#)°((not°nilp°x#)->*(y#=inc°y#)°(x#=pop°x#))°(y#=0&)°(id<-x)°id,nil) fios-Objekte regeln die Nebeneffekte Ein fios-Objekt ist abgeleitet vom dict-Objekt und hat den Aufbau: (fios :: self op arg param iof ) Nun dürfen innerhalb von Funktionen keine Nebeneffekte passieren (Referentielle Transparenz). Das fios-Objekt kann also nur innerhalb einer Funktion generiert werden und die Funktion wird mit dem Output des fios-Objekts beendet. Bis das fios-Objekt zur Toplevel-Ausgabe gelangt, doch zuvor wird es dort abgefangen und dem FIOSystem zur Bewertung übergeben. Aus den Instanzenwerten wird dann der entsprechende Nebeneffekt ermittelt. Die Auslösung des Nebeneffektes, das kann z.B. das Lesen einer Datei sein, führt zu einem refios-Objekt, das das Resultat (hier: Inhalt der Datei) und die Durchleitungsdaten enthält. Dieses refios-Objekt ist das Argument der Folgefunktion, mit der der Programmverlauf fortgesetzt wird. In Haskell wird dieser Sachverhalt mit der IO-Monade gelöst. Beispiel für den Gebrauch von fios-Objekten: timeloop:=((op;=time&),(iof;=((self;=console ::),(op;=output&),(arg;=result),(iof;=timeloop&),io)&),io) "wenn in der Ausführumgebung timeloop getippt wird, so wird die Uhrzeit in einer Endlosschleife angezeigt." Objektorientierung Definitionsschema für Klassenbezeichner mit Klassenobjekt: klasse1:=(class :: superklasse1 selektor1 methode1 selektor2 methode2 ... ... ) Definitionschema für Operatoren, Definition der Methoden und Konstanten: operator1:=(selektor1 eeop funktion_für_atomare_datentypen1) methode1:=(((1°self)*(1°arg))+((2°self)*(2°arg))) argument1:=(list :: 3 2) objektorientierte Anwendung: (klasse1 :: 12 15) operator1 argument1 --> 66 Operator-Argument Das oparg-Objekt wird angelegt, wenn ein Operator in einer Sequenz ausgeführt wird. "Hier mit der Funktion id als Operator:" (3 id 2):(string :: 'A' 'B' 'C') --> (oparg :: (3 id 2) (string :: 'ABC')) "mit self und arg kann man nun auf den Funktionsterm und das Argument zugreifen." Verwendet man einen eeop-Term statt id, so wird der linke und der rechte Teil auf das Argument angewendet, und daraus eine Liste erzeugt. (3 (nop eeop id) 2):(string :: 'A' 'B' 'C') --> (list :: 'CB') "mit self und arg kann auch hier auf das linke und rechte Resultat in der Liste zugegriffen werden." nop übrigends verhindert, dass in der Klasse des linken Resultats nach dem Selektor gesucht wird. variablenfrei versus lambda Variablen haben den Vorteil, dass dem zugeordneten Wert ein kurze Beschreibung gegeben werden kann, was der Dokumentation der Programme zugute kommt. Möchte man Variablen verwenden, wie beim Lambda-Kalkül, muss man sich allerdings für eine Bindungsstrategie entscheiden, der lexikalischen Bindung oder der dynamischen Bindung. Variablenfrei Programmieren bedeutet stattdessen Zahlen für die Positionen der Parameter zu gebrauchen, dadurch wird das mitzuführende Environment (Liste der lambda-Bindungen) durch ein kanalisiertes Argument ersetzt. Die Verwendung der dynamischen Bindung bringt sogar ein ernstzunehmendes Problem mit sich, dass nun in LISP dokumentiert werden soll, da es den Lambda-Kalkül unterstützt. Problem bei der dynamischen Bindung ; es wird ein LISP mit dynamischer Bindung verwendet (set 'pi 3.141592) (defun kreisflaeche ® (* pi (* r r))) ; noch läuft alles ok (kreisflaeche 10) --> 314.1592 ; es wird eine Erweiterung durchgeführt (defun kreisflaechemitdurchmesser (pi d) (kreisflaeche (/ d 2))) ; nun passiert etwas unerwartetes (kreisflaechemitdurchmesser 3 20) --> 300 ; Es kommt ein falsches Ergebnis zustande, was auf die dynamische Bindung zurückzuführen ist. ; Dies ist nur ein harmloses Beispiel, tatsächlich entstehen so schwer zu entdeckende Fehler. ; Man sollte daher die dynamische Bindung meiden. Evaluation zu FP-Systemen und Fpii Warum John Backus in seinem Aufsatz zu den FP-Systemen auf Variablen verzichtete ist nicht ganz klar. Vielleicht kannte er das Problem bei der dynamischen Bindung von Variablen. Vielleicht war es auch die Komposition aus der Funktionalanalysis und die anderen funktionalen Formen, die einen eleganten Programmaufbau ohne Variablen gestatteten (oder forderten); mit der Schnelligkeit von APL-Operatoren. Bei der Entwicklung von Fpii ist klar geworden, dass man auf Variablen verzichten kann, weil die dict-Objekte mit ihren Instanzenvariablen einen Ersatz bieten. Weblinks * Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs Stanford University, 1978 * Kritik am Backus-Aufsatz von Prof. Dr. E. W. Dijkstra * Beta einer Virtuellen Maschine für Fpii * FP-Blog Kategorie:Programmiersprache Kategorie:funktionale Programmiersprache Kategorie:objektorientierte Programmiersprache